package com.ze.tool.excel;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.WriteWorkbook;
import com.ze.tool.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * easyExcel数据导出模板
 */
@Slf4j
public abstract class BaseEasyExcelExport<Condition, ExportBean> {

    public static final long DEFAULT_EACH_SHEET_TOTAL_COUNT = 5000L;
    public static final long DEFAULT_EACH_TIMES_WRITE_SHEET_TOTAL_COUNT = 5000L;

    /**
     * 导出到excel，支持大数量的分批写入
     *
     * @param fileName
     * @param queryCondition 查询条件
     */
    @Deprecated
    protected void exportExcel(String fileName, Map<String, Object> queryCondition) {
        HttpServletResponse response = SpringContextUtils.getHttpServletResponse();
        //根据条件查询总记录
        Long totalCount = dataTotalCount(queryCondition);
        //每一个Sheet存放的记录数
        Long sheetDataRows = eachSheetTotalCount();
        //每次写入的数据量
        Long writeDataRows = eachTimesWriteSheetTotalCount();
        if (totalCount < sheetDataRows) {
            sheetDataRows = totalCount;
        }
        if (sheetDataRows < writeDataRows) {
            writeDataRows = sheetDataRows;
        }
        doExport(response, fileName, queryCondition, totalCount, sheetDataRows, writeDataRows);
    }

    /**
     * 导出到excel
     *
     * @param response
     * @param fileName
     * @param queryCondition 查询条件
     * @param totalCount     数量
     * @param sheetDataRows  整体每页写的条数
     * @param writeDataRows  每次写的条数
     */
    @Deprecated
    private void doExport(HttpServletResponse response, String fileName, Map<String, Object> queryCondition,
                          Long totalCount, Long sheetDataRows, Long writeDataRows) {
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            WriteWorkbook writeWorkbook = new WriteWorkbook();
            writeWorkbook.setOutputStream(outputStream);
            writeWorkbook.setExcelType(ExcelTypeEnum.XLSX);
            ExcelWriter writer = new ExcelWriter(writeWorkbook);
            WriteTable table = new WriteTable();
            table.setHead(getExcelHead());
            // 计算需要的Sheet数量
            Long sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            // 计算一般情况下每一个Sheet需要写入的次数
            Long oneSheetWriteCount = totalCount > sheetDataRows ? sheetDataRows / writeDataRows :
                    totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount / writeDataRows;
            // 计算最后一个sheet需要写入的次数
            Long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount :
                    (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) :
                            (totalCount / sheetDataRows / writeDataRows + 1));
            // 分批查询分次写入
            List<List<String>> dataList = new ArrayList<>();
            for (int i = 0; i < sheetNum; i++) {
                WriteSheet sheet = new WriteSheet();
                sheet.setSheetNo(i);
                sheet.setSheetName(sheetNum == 1 ? fileName : fileName + i);
                for (int j = 0; j < (i != sheetNum - 1 || i == 0 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                    // 清空减少内存量
                    dataList.clear();
                    buildDataList(dataList, queryCondition, j + 1 + oneSheetWriteCount * i, writeDataRows);
                    writer.write(dataList, sheet, table);
                }
            }
            response.setHeader("Content-Disposition", "attachment;filename="
                    + new String((fileName).getBytes("gb2312"),
                    "ISO-8859-1") + ".xlsx");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            writer.finish();
            outputStream.flush();
        } catch (Exception e) {
            log.error("AbstractEasyExcelExport.exportWithBigData.error:{}", e.getMessage(), e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    log.error("AbstractEasyExcelExport.exportWithBigData.close.error:{}", e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 获取导出的表头
     */
    @Deprecated
    protected List<List<String>> getExcelHead() {
        throw new RuntimeException("getExcelHead is deprecated");
    }

    /**
     * 计算导出数据的总数
     */
    @Deprecated
    protected Long dataTotalCount(Map<String, Object> conditions) {
        throw new RuntimeException("dataTotalCount is deprecated");
    }

    /**
     * 每一个sheet存放的数据总数
     */
    protected Long eachSheetTotalCount() {
        return DEFAULT_EACH_SHEET_TOTAL_COUNT;
    }

    /**
     * 每次写入sheet的总数
     */
    protected Long eachTimesWriteSheetTotalCount() {
        return DEFAULT_EACH_TIMES_WRITE_SHEET_TOTAL_COUNT;
    }

    /**
     * 构建每次查询数量
     */
    @Deprecated
    protected void buildDataList(List<List<String>> resultList, Map<String, Object> queryCondition,
                                 Long pageNo, Long pageSize) {
        throw new RuntimeException("buildDataList is deprecated");
    }

    /**
     * 导出数据
     */
    public void exportExcel(String fileName, Condition queryCondition) {

        HttpServletResponse response = SpringContextUtils.getHttpServletResponse();
        //根据条件查询总记录
        Long totalCount = dataTotalCount(queryCondition);
        //每一个Sheet存放的记录数
        Long sheetDataRows = eachSheetTotalCount();
        //每次写入的数据量
        Long writeDataRows = eachTimesWriteSheetTotalCount();
        if (totalCount < sheetDataRows) {
            sheetDataRows = totalCount;
        }
        if (sheetDataRows < writeDataRows) {
            writeDataRows = sheetDataRows;
        }
        doExport(response, fileName, queryCondition, totalCount, sheetDataRows, writeDataRows);

    }

    /**
     * 计算导出数据的总数
     *
     * @param conditions 查询条件
     * @return
     */
    protected Long dataTotalCount(Condition conditions) {
        throw new RuntimeException("must override dataTotalCount");
    }

    /**
     * 导出到excel
     */
    private void doExport(HttpServletResponse response, String fileName, Condition queryCondition,
                          Long totalCount, Long sheetDataRows, Long writeDataRows) {

        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            WriteWorkbook writeWorkbook = new WriteWorkbook();
            writeWorkbook.setOutputStream(outputStream);
            writeWorkbook.setExcelType(ExcelTypeEnum.XLSX);
            ExcelWriter writer = new ExcelWriter(writeWorkbook);
            WriteTable table = new WriteTable();

            // 获取导出实体bean的class
            Type genericSuperclass = this.getClass().getGenericSuperclass();
            ParameterizedType paramType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = paramType.getActualTypeArguments();
            Class<?> actualTypeArgument = (Class<?>) actualTypeArguments[1];
            Field[] fields = actualTypeArgument.getDeclaredFields();

            // 获取导出字段
            List<ExportFieldMeta> exportFieldMetas = new ArrayList<>();
            for (Field field : fields) {
                ExportField annotation = field.getAnnotation(ExportField.class);
                if (Objects.nonNull(annotation)) {
                    ExportFieldMeta meta = new ExportFieldMeta();
                    meta.setField(field);
                    meta.setTitle(annotation.title());
                    meta.setSort(annotation.sort());
                    exportFieldMetas.add(meta);
                }
            }
            exportFieldMetas = exportFieldMetas.stream().sorted().collect(Collectors.toList());
            List<List<String>> titles = exportFieldMetas.stream().map(item -> Collections.singletonList(item.getTitle())).collect(Collectors.toList());
            List<Field> exploreFiles = exportFieldMetas.stream().map(ExportFieldMeta::getField).peek(item -> item.setAccessible(true)).collect(Collectors.toList());
            table.setHead(titles);

            // 计算需要的Sheet数量
            Long sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            // 计算一般情况下每一个Sheet需要写入的次数
            Long oneSheetWriteCount = totalCount > sheetDataRows ? sheetDataRows / writeDataRows :
                    totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount / writeDataRows;
            // 计算最后一个sheet需要写入的次数
            Long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount :
                    (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) :
                            (totalCount / sheetDataRows / writeDataRows + 1));
            // 分批查询分次写入
            for (int i = 0; i < sheetNum; i++) {
                WriteSheet sheet = new WriteSheet();
                sheet.setSheetNo(i);
                sheet.setSheetName(sheetNum == 1 ? fileName : fileName + i);
                for (int j = 0; j < (i != sheetNum - 1 || i == 0 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                    // 通过字段获取值
                    List<ExportBean> exportBeanList = buildDataList(queryCondition, j + 1 + oneSheetWriteCount * i, writeDataRows);
                    if (exportBeanList != null && exportBeanList.size() > 0) {
                        List<List<String>> rows = exportBeanList.stream().map(item -> exploreFiles.stream().map(field -> {
                            String res = "";
                            try {
                                Object value = field.get(item);
                                if (Objects.nonNull(value)) {
                                    res = value.toString();
                                }
                            } catch (Exception e) {
                                log.error("AbstractEasyExcelExport.getField.error:{}", e.getMessage(), e);
                            }
                            return res;
                        }).collect(Collectors.toList())).collect(Collectors.toList());
                        writer.write(rows, sheet, table);
                    }
                }
            }
            response.setHeader("Content-Disposition", "attachment;filename="
                    + new String((fileName).getBytes("gb2312"),
                    "ISO-8859-1") + ".xlsx");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            writer.finish();
            outputStream.flush();
        } catch (Exception e) {
            log.error("AbstractEasyExcelExport.exportWithBigData.error:{}", e.getMessage(), e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    log.error("AbstractEasyExcelExport.exportWithBigData.close.error:{}", e.getMessage(), e);
                }
            }
        }

    }

    /**
     * 分批查询数据
     *
     * @param queryCondition 查询条件
     * @param pageNo         查询页码
     * @param pageSize       查询页数
     * @return
     */
    protected List<ExportBean> buildDataList(Condition queryCondition, Long pageNo, Long pageSize) {
        throw new RuntimeException("must override buildDataList");
    }
}